name: CI

on:
  push:
    # Run in PRs and for bors, but not on master.
    branches:
      - 'auto'
      - 'try'
  pull_request:
    branches:
      - 'master'
  schedule:
    - cron: '44 4 * * *' # At 4:44 UTC every day.

defaults:
  run:
    shell: bash

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            host_target: x86_64-unknown-linux-gnu
          - os: macos-latest
            host_target: x86_64-apple-darwin
          - os: windows-latest
            host_target: i686-pc-windows-msvc
    runs-on: ${{ matrix.os }}
    env:
      RUST_BACKTRACE: 1
      HOST_TARGET: ${{ matrix.host_target }}
    steps:
      - uses: actions/checkout@v3

      # Cache the global cargo directory, but NOT the local `target` directory which
      # we cannot reuse anyway when the nightly changes (and it grows quite large
      # over time).
      - name: Add cache for cargo
        id: cache
        uses: actions/cache@v3
        with:
          path: |
            # Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
            ~/.cargo/bin
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            # contains package information of crates installed via `cargo install`.
            ~/.cargo/.crates.toml
            ~/.cargo/.crates2.json
          key: ${{ runner.os }}-cargo-reset20230315-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-reset20230315

      - name: Install rustup-toolchain-install-master
        if: ${{ steps.cache.outputs.cache-hit != 'true' }}
        run: cargo install -f rustup-toolchain-install-master

      - name: Install "master" toolchain
        run: |
          if [[ ${{ github.event_name }} == 'schedule' ]]; then
            echo "Building against latest rustc git version"
            git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
          fi
          ./miri toolchain --host ${{ matrix.host_target }}

      - name: Show Rust version
        run: |
          rustup show
          rustc -Vv
          cargo -V

      - name: Test
        run: ./ci/ci.sh

  style:
    name: style checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      # This is exactly duplicated from above. GHA is pretty terrible when it comes
      # to avoiding code duplication.

      # Cache the global cargo directory, but NOT the local `target` directory which
      # we cannot reuse anyway when the nightly changes (and it grows quite large
      # over time).
      - name: Add cache for cargo
        id: cache
        uses: actions/cache@v3
        with:
          path: |
            # Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
            ~/.cargo/bin
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            # contains package information of crates installed via `cargo install`.
            ~/.cargo/.crates.toml
            ~/.cargo/.crates2.json
          key: ${{ runner.os }}-cargo-reset20230315-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-reset20230315

      - name: Install rustup-toolchain-install-master
        if: ${{ steps.cache.outputs.cache-hit != 'true' }}
        run: cargo install -f rustup-toolchain-install-master

      - name: Install "master" toolchain
        run: |
          if [[ ${{ github.event_name }} == 'schedule' ]]; then
            echo "Building against latest rustc git version"
            git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
          fi
          ./miri toolchain

      - name: Show Rust version
        run: |
          rustup show
          rustc -Vv
          cargo -V

      - name: rustfmt
        run: ./miri fmt --check
      - name: clippy
        run: ./miri clippy -- -D warnings
      - name: rustdoc
        run: RUSTDOCFLAGS="-Dwarnings" ./miri cargo doc --document-private-items

  # These jobs doesn't actually test anything, but they're only used to tell
  # bors the build completed, as there is no practical way to detect when a
  # workflow is successful listening to webhooks only.
  #
  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
  end-success:
    name: bors build finished
    runs-on: ubuntu-latest
    needs: [build, style]
    if: github.event.pusher.name == 'bors' && success()
    steps:
      - name: mark the job as a success
        run: exit 0
  end-failure:
    name: bors build finished
    runs-on: ubuntu-latest
    needs: [build, style]
    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
    steps:
      - name: mark the job as a failure
        run: exit 1

  cron-fail-notify:
    name: cronjob failure notification
    runs-on: ubuntu-latest
    needs: [build, style]
    if: github.event_name == 'schedule' && (failure() || cancelled())
    steps:
      # Send a Zulip notification
      - name: Install zulip-send
        run: pip3 install zulip
      - name: Send Zulip notification
        env:
          ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
          ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
        run: |
          ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
            --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
            --message 'Dear @*T-miri*,

          It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed.

          This likely means that rustc changed the miri directory and
          we now need to do a [`./miri rustc-pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo).

          Would you mind investigating this issue?

          Thanks in advance!
          Sincerely,
          The Miri Cronjobs Bot'

      # Attempt to auto-sync with rustc
      - uses: actions/checkout@v3
        with:
          fetch-depth: 256 # get a bit more of the history
      - name: install josh-proxy
        run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06
      - name: setup bot git name and email
        run: |
          git config --global user.name 'The Miri Conjob Bot'
          git config --global user.email 'miri@cron.bot'
      - name: get changes from rustc
        run: ./miri rustc-pull
      - name: Install rustup-toolchain-install-master
        run: cargo install -f rustup-toolchain-install-master
      - name: format changes (if any)
        run: |
          ./miri toolchain
          ./miri fmt --check || (./miri fmt && git commit -am "fmt")
      - name: Push changes to a branch
        run: |
          BRANCH="rustup-$(date -u +%Y-%m-%d)"
          git switch -c $BRANCH
          git push -u origin $BRANCH
      - name: Create Pull Request
        run: |
          PR=$(gh pr create -B master --title 'Automatic Rustup' --body '')
          ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
            --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
            --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
          ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
